From cb65b83e524e385265de3478c70e1c20b210263f Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20H=C3=A4rdeman?= Date: Fri, 28 Nov 2025 11:48:59 +0100 Subject: [PATCH] config: move pio json handling to statefiles.c MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The prefix information files are also examples of statefiles, and there are some things which can be shared between e.g. the state file logic and the pio file logic (like the conversion from MONOTIME to time_t and back, and also the use of tmp files). That's for later patches though, this just moves the code without any code changes to make subsequent patches clearer. Signed-off-by: David Härdeman Link: https://github.com/openwrt/odhcpd/pull/333 Signed-off-by: Álvaro Fernández Rojas --- src/config.c | 302 ----------------------------------------------- src/odhcpd.h | 2 - src/router.c | 1 + src/statefiles.c | 302 +++++++++++++++++++++++++++++++++++++++++++++++ src/statefiles.h | 5 + 5 files changed, 308 insertions(+), 304 deletions(-) diff --git a/src/config.c b/src/config.c index 97af99a..fb492b1 100644 --- a/src/config.c +++ b/src/config.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -12,7 +11,6 @@ #include #include -#include #include #include #include @@ -1996,306 +1994,6 @@ static int ipv6_pxe_from_uci(struct uci_section* s) return ipv6_pxe_entry_new(arch, url) ? -1 : 0; } -#define JSON_LENGTH "length" -#define JSON_PREFIX "prefix" -#define JSON_SLAAC "slaac" -#define JSON_TIME "time" - -static inline time_t config_time_from_json(time_t json_time) -{ - time_t ref, now; - - ref = time(NULL); - now = odhcpd_time(); - - if (now > json_time || ref > json_time) - return 0; - - return json_time + (now - ref); -} - -static inline time_t config_time_to_json(time_t config_time) -{ - time_t ref, now; - - ref = time(NULL); - now = odhcpd_time(); - - return config_time + (ref - now); -} - -static inline bool config_ra_pio_enabled(struct interface *iface) -{ - return config.ra_piofolder_fd >= 0 && iface->ra == MODE_SERVER && !iface->master; -} - -static bool config_ra_pio_time(json_object *slaac_json, time_t *slaac_time) -{ - time_t pio_json_time, pio_time; - json_object *time_json; - - time_json = json_object_object_get(slaac_json, JSON_TIME); - if (!time_json) - return true; - - pio_json_time = (time_t) json_object_get_int64(time_json); - if (!pio_json_time) - return true; - - pio_time = config_time_from_json(pio_json_time); - if (!pio_time) - return false; - - *slaac_time = pio_time; - - return true; -} - -static json_object *config_load_ra_pio_json(struct interface *iface) -{ - json_object *json; - int fd; - - fd = openat(config.ra_piofolder_fd, iface->ifname, O_RDONLY | O_CLOEXEC); - if (fd < 0) - return NULL; - - json = json_object_from_fd(fd); - - close(fd); - - if (!json) - error("rfc9096: %s: json read error %s", - iface->ifname, - json_util_get_last_err()); - - return json; -} - -void config_load_ra_pio(struct interface *iface) -{ - json_object *json, *slaac_json; - struct ra_pio *new_pios; - size_t pio_cnt; - time_t now; - - if (!config_ra_pio_enabled(iface)) - return; - - json = config_load_ra_pio_json(iface); - if (!json) - return; - - slaac_json = json_object_object_get(json, JSON_SLAAC); - if (!slaac_json) { - json_object_put(json); - return; - } - - now = odhcpd_time(); - - pio_cnt = json_object_array_length(slaac_json); - new_pios = realloc(iface->pios, sizeof(struct ra_pio) * pio_cnt); - if (!new_pios) { - json_object_put(json); - return; - } - - iface->pios = new_pios; - iface->pio_cnt = 0; - for (size_t i = 0; i < pio_cnt; i++) { - json_object *cur_pio_json, *length_json, *prefix_json; - const char *pio_str; - time_t pio_lt = 0; - struct ra_pio *pio; - uint8_t pio_len; - - cur_pio_json = json_object_array_get_idx(slaac_json, i); - if (!cur_pio_json) - continue; - - if (!config_ra_pio_time(cur_pio_json, &pio_lt)) - continue; - - length_json = json_object_object_get(cur_pio_json, JSON_LENGTH); - if (!length_json) - continue; - - prefix_json = json_object_object_get(cur_pio_json, JSON_PREFIX); - if (!prefix_json) - continue; - - pio_len = (uint8_t) json_object_get_uint64(length_json); - pio_str = json_object_get_string(prefix_json); - pio = &iface->pios[iface->pio_cnt]; - - inet_pton(AF_INET6, pio_str, &pio->prefix); - pio->length = pio_len; - pio->lifetime = pio_lt; - info("rfc9096: %s: load %s/%u (%u)", - iface->ifname, - pio_str, - pio_len, - ra_pio_lifetime(pio, now)); - - iface->pio_cnt++; - } - - json_object_put(json); - - if (!iface->pio_cnt) { - free(iface->pios); - iface->pios = NULL; - } else if (iface->pio_cnt != pio_cnt) { - struct ra_pio *tmp; - - tmp = realloc(iface->pios, sizeof(struct ra_pio) * iface->pio_cnt); - if (tmp) - iface->pios = tmp; - } -} - -static void config_save_ra_pio_json(struct interface *iface, struct json_object *json) -{ - size_t tmp_piofile_strlen; - char *tmp_piofile; - int fd, ret; - - tmp_piofile_strlen = strlen(iface->ifname) + 2; - tmp_piofile = alloca(tmp_piofile_strlen); - snprintf(tmp_piofile, tmp_piofile_strlen, ".%s", iface->ifname); - - fd = openat(config.ra_piofolder_fd, - tmp_piofile, - O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, - 0644); - if (fd < 0) { - error("rfc9096: %s: error %m creating temporary json file", - iface->ifname); - return; - } - - ret = json_object_to_fd(fd, json, JSON_C_TO_STRING_PLAIN); - if (ret) { - error("rfc9096: %s: json write error %s", - iface->ifname, - json_util_get_last_err()); - close(fd); - unlinkat(config.ra_piofolder_fd, tmp_piofile, 0); - return; - } - - ret = fsync(fd); - if (ret) { - error("rfc9096: %s: error %m syncing %s", - iface->ifname, - tmp_piofile); - close(fd); - unlinkat(config.ra_piofolder_fd, tmp_piofile, 0); - return; - } - - ret = close(fd); - if (ret) { - error("rfc9096: %s: error %m closing %s", - iface->ifname, - tmp_piofile); - unlinkat(config.ra_piofolder_fd, tmp_piofile, 0); - return; - } - - ret = renameat(config.ra_piofolder_fd, - tmp_piofile, - config.ra_piofolder_fd, - iface->ifname); - if (ret) { - error("rfc9096: %s: error %m renaming piofile: %s -> %s", - iface->ifname, - tmp_piofile, - iface->ifname); - close(fd); - unlinkat(config.ra_piofolder_fd, tmp_piofile, 0); - return; - } - - iface->pio_update = false; - warn("rfc9096: %s: piofile updated", iface->ifname); -} - -void config_save_ra_pio(struct interface *iface) -{ - struct json_object *json, *slaac_json; - char ipv6_str[INET6_ADDRSTRLEN]; - time_t now; - - if (!config_ra_pio_enabled(iface)) - return; - - if (!iface->pio_update) - return; - - now = odhcpd_time(); - - json = json_object_new_object(); - if (!json) - return; - - slaac_json = json_object_new_array_ext(iface->pio_cnt); - if (!slaac_json) { - json_object_put(slaac_json); - return; - } - - json_object_object_add(json, JSON_SLAAC, slaac_json); - - for (size_t i = 0; i < iface->pio_cnt; i++) { - struct json_object *cur_pio_json, *len_json, *pfx_json; - const struct ra_pio *cur_pio = &iface->pios[i]; - - if (ra_pio_expired(cur_pio, now)) - continue; - - cur_pio_json = json_object_new_object(); - if (!cur_pio_json) - continue; - - inet_ntop(AF_INET6, &cur_pio->prefix, ipv6_str, sizeof(ipv6_str)); - - pfx_json = json_object_new_string(ipv6_str); - if (!pfx_json) { - json_object_put(cur_pio_json); - continue; - } - - len_json = json_object_new_uint64(cur_pio->length); - if (!len_json) { - json_object_put(cur_pio_json); - json_object_put(pfx_json); - continue; - } - - json_object_object_add(cur_pio_json, JSON_PREFIX, pfx_json); - json_object_object_add(cur_pio_json, JSON_LENGTH, len_json); - - if (cur_pio->lifetime) { - struct json_object *time_json; - time_t pio_lt; - - pio_lt = config_time_to_json(cur_pio->lifetime); - - time_json = json_object_new_int64(pio_lt); - if (time_json) - json_object_object_add(cur_pio_json, JSON_TIME, time_json); - } - - json_object_array_add(slaac_json, cur_pio_json); - } - - config_save_ra_pio_json(iface, json); - - json_object_put(json); -} - void odhcpd_reload(void) { struct uci_context *uci = uci_alloc_context(); diff --git a/src/odhcpd.h b/src/odhcpd.h index 0b308b2..3ddbb0c 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -577,8 +577,6 @@ struct lease_cfg *config_find_lease_cfg_by_mac(const uint8_t *mac); struct lease_cfg *config_find_lease_cfg_by_hostid(const uint64_t hostid); struct lease_cfg *config_find_lease_cfg_by_ipv4(const struct in_addr ipv4); int config_set_lease_cfg_from_blobmsg(struct blob_attr *ba); -void config_load_ra_pio(struct interface *iface); -void config_save_ra_pio(struct interface *iface); #ifdef WITH_UBUS int ubus_init(void); diff --git a/src/router.c b/src/router.c index afb64ae..9dde823 100644 --- a/src/router.c +++ b/src/router.c @@ -28,6 +28,7 @@ #include "router.h" #include "odhcpd.h" +#include "statefiles.h" static void forward_router_solicitation(const struct interface *iface); diff --git a/src/statefiles.c b/src/statefiles.c index 21201cb..a8ab28b 100644 --- a/src/statefiles.c +++ b/src/statefiles.c @@ -4,6 +4,7 @@ * SPDX-FileCopyrightText: 2022 Kevin Darbyshire-Bryant * SPDX-FileCopyrightText: 2024 Paul Donald * SPDX-FileCopyrightText: 2024 David Härdeman + * SPDX-FileCopyrightText: 2025 Álvaro Fernández Rojas * * SPDX-License-Identifier: GPL2.0-only */ @@ -25,6 +26,7 @@ #include #include +#include #include "odhcpd.h" #include "dhcpv6-ia.h" @@ -99,6 +101,306 @@ static void statefiles_finish_tmp_file(int dirfd, FILE **fpp, const char *prefix renameat(dirfd, ODHCPD_TMP_FILE, dirfd, filename); } +#define JSON_LENGTH "length" +#define JSON_PREFIX "prefix" +#define JSON_SLAAC "slaac" +#define JSON_TIME "time" + +static inline time_t config_time_from_json(time_t json_time) +{ + time_t ref, now; + + ref = time(NULL); + now = odhcpd_time(); + + if (now > json_time || ref > json_time) + return 0; + + return json_time + (now - ref); +} + +static inline time_t config_time_to_json(time_t config_time) +{ + time_t ref, now; + + ref = time(NULL); + now = odhcpd_time(); + + return config_time + (ref - now); +} + +static inline bool config_ra_pio_enabled(struct interface *iface) +{ + return config.ra_piofolder_fd >= 0 && iface->ra == MODE_SERVER && !iface->master; +} + +static bool config_ra_pio_time(json_object *slaac_json, time_t *slaac_time) +{ + time_t pio_json_time, pio_time; + json_object *time_json; + + time_json = json_object_object_get(slaac_json, JSON_TIME); + if (!time_json) + return true; + + pio_json_time = (time_t) json_object_get_int64(time_json); + if (!pio_json_time) + return true; + + pio_time = config_time_from_json(pio_json_time); + if (!pio_time) + return false; + + *slaac_time = pio_time; + + return true; +} + +static json_object *config_load_ra_pio_json(struct interface *iface) +{ + json_object *json; + int fd; + + fd = openat(config.ra_piofolder_fd, iface->ifname, O_RDONLY | O_CLOEXEC); + if (fd < 0) + return NULL; + + json = json_object_from_fd(fd); + + close(fd); + + if (!json) + error("rfc9096: %s: json read error %s", + iface->ifname, + json_util_get_last_err()); + + return json; +} + +void config_load_ra_pio(struct interface *iface) +{ + json_object *json, *slaac_json; + struct ra_pio *new_pios; + size_t pio_cnt; + time_t now; + + if (!config_ra_pio_enabled(iface)) + return; + + json = config_load_ra_pio_json(iface); + if (!json) + return; + + slaac_json = json_object_object_get(json, JSON_SLAAC); + if (!slaac_json) { + json_object_put(json); + return; + } + + now = odhcpd_time(); + + pio_cnt = json_object_array_length(slaac_json); + new_pios = realloc(iface->pios, sizeof(struct ra_pio) * pio_cnt); + if (!new_pios) { + json_object_put(json); + return; + } + + iface->pios = new_pios; + iface->pio_cnt = 0; + for (size_t i = 0; i < pio_cnt; i++) { + json_object *cur_pio_json, *length_json, *prefix_json; + const char *pio_str; + time_t pio_lt = 0; + struct ra_pio *pio; + uint8_t pio_len; + + cur_pio_json = json_object_array_get_idx(slaac_json, i); + if (!cur_pio_json) + continue; + + if (!config_ra_pio_time(cur_pio_json, &pio_lt)) + continue; + + length_json = json_object_object_get(cur_pio_json, JSON_LENGTH); + if (!length_json) + continue; + + prefix_json = json_object_object_get(cur_pio_json, JSON_PREFIX); + if (!prefix_json) + continue; + + pio_len = (uint8_t) json_object_get_uint64(length_json); + pio_str = json_object_get_string(prefix_json); + pio = &iface->pios[iface->pio_cnt]; + + inet_pton(AF_INET6, pio_str, &pio->prefix); + pio->length = pio_len; + pio->lifetime = pio_lt; + info("rfc9096: %s: load %s/%u (%u)", + iface->ifname, + pio_str, + pio_len, + ra_pio_lifetime(pio, now)); + + iface->pio_cnt++; + } + + json_object_put(json); + + if (!iface->pio_cnt) { + free(iface->pios); + iface->pios = NULL; + } else if (iface->pio_cnt != pio_cnt) { + struct ra_pio *tmp; + + tmp = realloc(iface->pios, sizeof(struct ra_pio) * iface->pio_cnt); + if (tmp) + iface->pios = tmp; + } +} + +static void config_save_ra_pio_json(struct interface *iface, struct json_object *json) +{ + size_t tmp_piofile_strlen; + char *tmp_piofile; + int fd, ret; + + tmp_piofile_strlen = strlen(iface->ifname) + 2; + tmp_piofile = alloca(tmp_piofile_strlen); + snprintf(tmp_piofile, tmp_piofile_strlen, ".%s", iface->ifname); + + fd = openat(config.ra_piofolder_fd, + tmp_piofile, + O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, + 0644); + if (fd < 0) { + error("rfc9096: %s: error %m creating temporary json file", + iface->ifname); + return; + } + + ret = json_object_to_fd(fd, json, JSON_C_TO_STRING_PLAIN); + if (ret) { + error("rfc9096: %s: json write error %s", + iface->ifname, + json_util_get_last_err()); + close(fd); + unlinkat(config.ra_piofolder_fd, tmp_piofile, 0); + return; + } + + ret = fsync(fd); + if (ret) { + error("rfc9096: %s: error %m syncing %s", + iface->ifname, + tmp_piofile); + close(fd); + unlinkat(config.ra_piofolder_fd, tmp_piofile, 0); + return; + } + + ret = close(fd); + if (ret) { + error("rfc9096: %s: error %m closing %s", + iface->ifname, + tmp_piofile); + unlinkat(config.ra_piofolder_fd, tmp_piofile, 0); + return; + } + + ret = renameat(config.ra_piofolder_fd, + tmp_piofile, + config.ra_piofolder_fd, + iface->ifname); + if (ret) { + error("rfc9096: %s: error %m renaming piofile: %s -> %s", + iface->ifname, + tmp_piofile, + iface->ifname); + close(fd); + unlinkat(config.ra_piofolder_fd, tmp_piofile, 0); + return; + } + + iface->pio_update = false; + warn("rfc9096: %s: piofile updated", iface->ifname); +} + +void config_save_ra_pio(struct interface *iface) +{ + struct json_object *json, *slaac_json; + char ipv6_str[INET6_ADDRSTRLEN]; + time_t now; + + if (!config_ra_pio_enabled(iface)) + return; + + if (!iface->pio_update) + return; + + now = odhcpd_time(); + + json = json_object_new_object(); + if (!json) + return; + + slaac_json = json_object_new_array_ext(iface->pio_cnt); + if (!slaac_json) { + json_object_put(slaac_json); + return; + } + + json_object_object_add(json, JSON_SLAAC, slaac_json); + + for (size_t i = 0; i < iface->pio_cnt; i++) { + struct json_object *cur_pio_json, *len_json, *pfx_json; + const struct ra_pio *cur_pio = &iface->pios[i]; + + if (ra_pio_expired(cur_pio, now)) + continue; + + cur_pio_json = json_object_new_object(); + if (!cur_pio_json) + continue; + + inet_ntop(AF_INET6, &cur_pio->prefix, ipv6_str, sizeof(ipv6_str)); + + pfx_json = json_object_new_string(ipv6_str); + if (!pfx_json) { + json_object_put(cur_pio_json); + continue; + } + + len_json = json_object_new_uint64(cur_pio->length); + if (!len_json) { + json_object_put(cur_pio_json); + json_object_put(pfx_json); + continue; + } + + json_object_object_add(cur_pio_json, JSON_PREFIX, pfx_json); + json_object_object_add(cur_pio_json, JSON_LENGTH, len_json); + + if (cur_pio->lifetime) { + struct json_object *time_json; + time_t pio_lt; + + pio_lt = config_time_to_json(cur_pio->lifetime); + + time_json = json_object_new_int64(pio_lt); + if (time_json) + json_object_object_add(cur_pio_json, JSON_TIME, time_json); + } + + json_object_array_add(slaac_json, cur_pio_json); + } + + config_save_ra_pio_json(iface, json); + + json_object_put(json); +} + static void statefiles_write_host(const char *ipbuf, const char *hostname, struct write_ctxt *ctxt) { char exp_dn[DNS_MAX_NAME_LEN]; diff --git a/src/statefiles.h b/src/statefiles.h index de71397..0b71509 100644 --- a/src/statefiles.h +++ b/src/statefiles.h @@ -1,5 +1,6 @@ /* * SPDX-FileCopyrightText: 2024 David Härdeman + * SPDX-FileCopyrightText: 2025 Álvaro Fernández Rojas * * SPDX-License-Identifier: GPL2.0-only */ @@ -10,6 +11,10 @@ #define ODHCPD_HOSTS_FILE_PREFIX "odhcpd.hosts" #define ODHCPD_TMP_FILE ".odhcpd.tmp" +void config_load_ra_pio(struct interface *iface); + +void config_save_ra_pio(struct interface *iface); + bool statefiles_write(void); void statefiles_setup_dirfd(const char *path, int *dirfdp); -- 2.30.2